home *** CD-ROM | disk | FTP | other *** search
/ Info-Mac 3 / Info_Mac_1994-01.iso / Development / Information / Mac Programming Secrets 1.0.1 / Chapter 10 / SpinningCursor.c < prev    next >
C/C++ Source or Header  |  1992-05-19  |  11KB  |  333 lines

  1. #include "SpinningCursor.h"
  2. #include "Retrace.h"
  3.  
  4. /*******************************************************************************
  5.  
  6.     Structure defining the 'acur' resource. Note that this resource as defined
  7.     on disk actually has the following structure:
  8.  
  9.         typedef struct {
  10.             unsigned short     numCursors;
  11.             unsigned short     index;
  12.             struct {
  13.                 short ID;
  14.                 short unused;
  15.             } cursors[1];
  16.         } Acur, *AcurPtr, **AcurHandle;
  17.  
  18.     However, we change this structure to the following in InitCursorCtl.
  19.  
  20. *******************************************************************************/
  21.  
  22. typedef struct {
  23.     unsigned short     numCursors;        /* Number of cursors */
  24.     unsigned short     index;            /* Next cursor to show */
  25.     CursHandle        cursors[1];        /* Actually unbounded array */
  26. } Acur, *AcurPtr, **AcurHandle;
  27.  
  28.  
  29. /*******************************************************************************
  30.  
  31.     Record used for our VBL task. This record is the same as the normal VBL
  32.     task record, except that we’ve added a field at the end. This extra field
  33.     holds our application’s A5 value. Our VBL task can then retrieve this
  34.     value when it wants to access our global variables.
  35.  
  36. *******************************************************************************/
  37.  
  38. typedef struct {
  39.     VBLTask        theTask;
  40.     long        A5;
  41. } VBLTaskWithA5, *VBLTaskWithA5Ptr;
  42.  
  43.  
  44. /*******************************************************************************
  45.  
  46.     Constants and variables
  47.  
  48. *******************************************************************************/
  49.  
  50. AcurHandle            pCursors;        /* Handle to the 'acur' resource. */
  51. VBLTaskWithA5Ptr    pCursorTask;    /* Pointer to the VBL task record. */
  52. short                pDesiredCount;    /* Value to be placed in the vblCount field
  53.                                        of our VBL task record. */
  54.  
  55. char CrsrBusy : 0x08CD;                /* Pointer to CrsrBusy low-memory global. */
  56.  
  57.  
  58. /*******************************************************************************
  59.  
  60.     InitCursorCtl
  61.  
  62.     Call this routine early in your program to initialize some structures.
  63.     After this routine reads in the 'acur' resource, it looks at the data
  64.     inside to find out what 'CURS' resources to read. At this point, the
  65.     'acur' resource looks like this:
  66.  
  67.         short    number of cursors
  68.         short    index of next cursor to show
  69.         short    resource ID of first cursor
  70.         short    <unused>
  71.         short    resource ID of second cursor
  72.         short    <unused
  73.         ...
  74.         short    resource ID of last cursor
  75.         short    <unused>
  76.  
  77.     When InitCursorCtl reads in a 'CURS' resource, it places the handle to the
  78.     cursor into the space that used to hold the ID of the resource. This means
  79.     that the above structure ends up looking like the following after all
  80.     cursors have been read in:
  81.  
  82.         short    number of cursors
  83.         short    index of next cursor to show
  84.         long    handle to first cursor
  85.         long    handle to second cursor
  86.         ...
  87.         long    handle to last cursor
  88.  
  89.     This function does _not_ detach any of the resources from the resource
  90.     fork. Although we change the 'acur' resource, we never call
  91.     ChangedResource on it, so the changes never get written out to disk.
  92.     However, make sure that the 'acur' resource is not marked as purgable or
  93.     our changes will get wiped out.
  94.  
  95. *******************************************************************************/
  96. void InitCursorCtl(short resID)
  97. {
  98.     short        cursorCount;
  99.     CursHandle*    workPtr;
  100.  
  101.     pCursors = (AcurHandle) GetResource('acur', resID);
  102.  
  103.     cursorCount = (**pCursors).numCursors;
  104.     (**pCursors).numCursors *= 32;
  105.     (**pCursors).index = 0;
  106.  
  107.     HLock((Handle) pCursors);
  108.     workPtr = (**pCursors).cursors;
  109.     while (cursorCount--) {
  110.         *workPtr++ = (CursHandle) GetResource('CURS', *(short*) workPtr);
  111.     }
  112.     HUnlock((Handle) pCursors);
  113. }
  114.  
  115.  
  116. /*******************************************************************************
  117.  
  118.     StartAsyncSpinning
  119.  
  120.     We call this routine to install a VBL task that calls SpinCursor at
  121.     regular intervals, regardless of whatever else is going on. This way, we
  122.     don’t have to worry about calling SpinCursor by hand in the middle of
  123.     calculations or any other lengthy process.
  124.  
  125.     Before starting the VBL task, we lock down all of the cursor data that
  126.     we’ll access in the task. Next, we we fill out all of the fields of the
  127.     VBL task record. Because we’ll need to access some global variables in the
  128.     VBL task procedure, we define an extended VBL task record with space for
  129.     our A5 register at the end. After all the fields are filled out, we simply
  130.     call VInstall.
  131.  
  132.     Per Apple’s Technote #180, our VBL routine will only be called when our
  133.     application is in the foreground. That’s OK, since we don’t want to spin
  134.     the cursor while we’re in the background. However, at some time in your
  135.     life, you may want to install a VBL routine that executes when your
  136.     application is in the background. The solution to this is to install the
  137.     VBL task in the System heap. Apple’s documentation is not too clear on
  138.     whether this means the VBL task _record_ or the VBL task _procedure_. To
  139.     play it safe, you might want to install both in the System heap.
  140.  
  141.     To do this easily, define the following extended VBL task record:
  142.  
  143.         typedef struct {
  144.             VBLTask        theTask;
  145.             long        A5;
  146.             short        Jmp;
  147.             void*        Routine;
  148.         } VBLTaskWithA5Sys, *VBLTaskWithA5SysPtr;
  149.  
  150.     This is the same as the extended record we defined at the beginning of
  151.     this file, with two additional fields. These two fields hold a stub VBL
  152.     function that merely jumps to the real VBL function back in your
  153.     application. Here is some code the demonstrates how to use this new
  154.     record:
  155.  
  156.         pCursorTask = (VBLTaskWithA5SysPtr)
  157.                             NewPtrSys(sizeof(VBLTaskWithA5Sys));    // CHANGED
  158.         pCursorTask->theTask.qType = vType;
  159.         pCursorTask->theTask.vblAddr = (ProcPtr) &pCursorTask->Jmp;    // CHANGED
  160.         pCursorTask->theTask.vblCount = pDesiredCount;
  161.         pCursorTask->theTask.vblPhase = 0;
  162.         pCursorTask->A5 = (long) CurrentA5;
  163.         pCursorTask->Jmp = 0x4EF9;                // JMP #Routine     // CHANGED
  164.         pCursorTask->Routine = MySpinner;                            // CHANGED
  165.  
  166.         if (HasFlushRoutines()) {
  167.             FlushDataCache();
  168.             FlushInstructionCache();
  169.         }
  170.  
  171.     Note that setting the last two fields to 68000 instructions constitutes
  172.     “self-modifying code”. OK, so it’s not really _self_ modifying, but it
  173.     fulfills the necessary criteria of cutting instructions from whole cloth.
  174.     For various reasons, this is bad, especially on any Macintosh with a 68040
  175.     in it. Apple has some instruction and data cache flushing routines which
  176.     you must call to avoid the problems of self-modifying code. Make sure that
  177.     you call them when they’re available.
  178.  
  179. *******************************************************************************/
  180. void    StartAsyncSpinning(short period)
  181. {
  182.     LockCursorData();
  183.  
  184.     pDesiredCount = period;
  185.  
  186.     pCursorTask = (VBLTaskWithA5Ptr) NewPtr(sizeof(VBLTaskWithA5));
  187.     pCursorTask->theTask.qType = vType;
  188.     pCursorTask->theTask.vblAddr = (ProcPtr) MySpinner;
  189.     pCursorTask->theTask.vblCount = pDesiredCount;
  190.     pCursorTask->theTask.vblPhase = 0;
  191.     pCursorTask->A5 = (long) CurrentA5;
  192.  
  193.     (void) VInstall((QElemPtr) pCursorTask);
  194. }
  195.  
  196.  
  197. /*******************************************************************************
  198.  
  199.     StopAsyncSpinning
  200.  
  201.     Remove the VBL task that spins the cursor and unlock all of the data it
  202.     used.
  203.  
  204. *******************************************************************************/
  205. void    StopAsyncSpinning()
  206. {
  207.     (void) VRemove((QElemPtr) pCursorTask);
  208.     DisposePtr((Ptr) pCursorTask);
  209.     UnlockCursorData();
  210. }
  211.  
  212.  
  213. /*******************************************************************************
  214.  
  215.     SpinCursor
  216.  
  217.     Unless you are using the asynchronous routines, you should call this
  218.     function at regular intervals during a lengthy process. Our “spin index”
  219.     is incremented by the amount passed in. In keeping with MPW’s
  220.     implementation, we only change the cursor every time the index passes
  221.     through a multiple of 32.
  222.  
  223. *******************************************************************************/
  224. void SpinCursor(short increment)
  225. {
  226.     short    oldIndex;
  227.     short    newIndex;
  228.  
  229.     oldIndex = (**pCursors).index / 32;
  230.  
  231.     (**pCursors).index += increment;
  232.     (**pCursors).index %= (**pCursors).numCursors;    // Take care of wraparound
  233.  
  234.     newIndex = (**pCursors).index / 32;
  235.  
  236.     if (newIndex != oldIndex) {
  237.         SetCursor(*(**pCursors).cursors[newIndex]);
  238.     }
  239. }
  240.  
  241.  
  242. /*******************************************************************************
  243.  
  244.     LockCursorData
  245.  
  246.     Lock all of the handles used to define our spinning cursor set. These
  247.     handles consist of our 'acur' resource, which in turn is essentially an
  248.     array of handles to our cursors. Each of these is moved high in memory and
  249.     locked down using HLockHi (this call is new to MPW 3.2 and THINK C 5.0,
  250.     but it will work on any Macintosh).
  251.  
  252. *******************************************************************************/
  253. void LockCursorData()
  254. {
  255.     short    cursorCount;
  256.     CursHandle*    workPtr;
  257.  
  258.     cursorCount = (**pCursors).numCursors / 32;
  259.  
  260.     HLockHi((Handle) pCursors);
  261.     workPtr = (**pCursors).cursors;
  262.     while (cursorCount--) {
  263.         HLockHi((Handle) *workPtr++);
  264.     }
  265. }
  266.  
  267.  
  268. /*******************************************************************************
  269.  
  270.     UnlockCursorData
  271.  
  272.     Unlock the data locked by LockCursorData. We call this after turning off
  273.     our asynchronous cursor spinning routines so that we don’t keep the data
  274.     locked in memory.
  275.  
  276. *******************************************************************************/
  277. void UnlockCursorData()
  278. {
  279.     short        cursorCount;
  280.     CursHandle*    workPtr;
  281.  
  282.     cursorCount = (**pCursors).numCursors / 32;
  283.  
  284.     workPtr = (**pCursors).cursors;
  285.     while (cursorCount--) {
  286.         HUnlock((Handle) *workPtr++);
  287.     }
  288.     HUnlock((Handle) pCursors);
  289. }
  290.  
  291.  
  292. /*******************************************************************************
  293.  
  294.     MySpinner
  295.  
  296.     VBL task procedure. This function is called at regular intervals to spin
  297.     the cursor. It first checks the low-memory global CrsrBusy to make sure
  298.     that the cursor is available to be changed. If so, we point A5 at our
  299.     application’s globals, call SpinCursor to change to the next cursor,
  300.     reprime our VBL task so it gets called again, and restore A5 to its
  301.     original value.
  302.  
  303.     Note that the way we look at the low-memory global CrsrBusy is very THINK
  304.     C-ish. THINK provides access to low-memory globals using a non-standard
  305.     syntax. At the start of this file, we declared CrsrBusy to be:
  306.  
  307.             char CrsrBusy : 0x08CD;
  308.  
  309.     We could then use it as shown in MySpinner. Under MPW C, we would have to
  310.     do the following:
  311.  
  312.             #define CrsrBusy 0x08CD
  313.             if ( *(char *) CrsrBusy == 0) { ... };
  314.  
  315. *******************************************************************************/
  316. void    MySpinner()
  317. {
  318.     long                oldA5;
  319.     VBLTaskWithA5Ptr    myTaskPtr;
  320.  
  321.     asm {
  322.         move.l    A0, myTaskPtr
  323.     }
  324.  
  325.     if (CrsrBusy == 0) {
  326.         oldA5 = SetA5(myTaskPtr->A5);
  327.         SpinCursor(32);
  328.         myTaskPtr->theTask.vblCount = pDesiredCount;
  329.         (void) SetA5(oldA5);
  330.     }
  331. }
  332.  
  333.